bitkeeper revision 1.800 (4055ee59_zI1OKOsC2EKXBIYFm3OtA)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 15 Mar 2004 17:56:41 +0000 (17:56 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 15 Mar 2004 17:56:41 +0000 (17:56 +0000)
console_client.py, __init__.py, setup.py, Makefile:
  new file
Many files:
  New console-terminal client in xenctl.console_client Python package. New option to xc_dom_create (-c on cmdline or auto_console in defaults file) to automatically become a console client.
utils.py:
  Rename: tools/xc/py/XenoUtil.py -> tools/xenctl/lib/utils.py

19 files changed:
.rootkeys
README.CD
docs/VBD-HOWTO.txt
docs/Xeno-HOWTO.txt
tools/Makefile
tools/examples/README
tools/examples/defaults
tools/examples/democd
tools/examples/netbsd
tools/examples/xc_dom_control.py
tools/examples/xc_dom_create.py
tools/examples/xc_vd_tool.py
tools/xc/py/XenoUtil.py [deleted file]
tools/xc/py/setup.py
tools/xenctl/Makefile [new file with mode: 0644]
tools/xenctl/lib/__init__.py [new file with mode: 0644]
tools/xenctl/lib/console_client.py [new file with mode: 0644]
tools/xenctl/lib/utils.py [new file with mode: 0644]
tools/xenctl/setup.py [new file with mode: 0644]

index 55b25752477126caccf093c00e03aedf36b22cb1..c21b6eebe4afb536bedc9e2b50e42b75cde71abb 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3fbba6dc38q-ioRlwSR_quw4G3qUeQ tools/xc/lib/xc_vif.c
 3fbd0a3dTwnDcfdw0-v46dPbX98zDw tools/xc/py/Makefile
 3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c
-3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
 3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
+4055ee41IfFazrwadCH2J72nz-A9YA tools/xenctl/Makefile
+4055ee4b_4Rvns_KzE12csI14EKK6Q tools/xenctl/lib/__init__.py
+4055ee4dwy4l0MghZosxoiu6zmhc9Q tools/xenctl/lib/console_client.py
+3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xenctl/lib/utils.py
+4055ee44Bu6oP7U0WxxXypbUt4dNPQ tools/xenctl/setup.py
 40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile
 4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py
 4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend/lib/console.py
index e9d172f268e08147d5e98fde8b8ec6e7b1d38d6a..2beb6fb1507e4ad8f27b40e9b6dc3f1a741ed864 100644 (file)
--- a/README.CD
+++ b/README.CD
@@ -464,11 +464,6 @@ You can rebuild Xen and the tools by typing 'make'. You can install
 them to the standard directories with 'make install', or into the
 ../install subtree with 'make dist'.
 
-/usr/local/bin/xc_*    the domain control tools
-/lib/libxc.so          the xc library
-/usr/lib/python2.2/site-packages/XenoUtil.py   python util library
-/usr/lib/python2.2/site-packages/Xc.c          python xc bindings
-
 If you're using the virtual disk control tools (xc_vd_tool) you'll
 need the SQLite library and python binding pysqlite.  There's a tar
 ball containing the necessary binaries on the project downloads page.
index 870ea2e040604b39c2b8891c58f796ca89f61962..b6feb104c23ff5ea014ff60f8a8b8b6128fd1d2f 100644 (file)
@@ -152,7 +152,7 @@ freespace -      Get the free space (in megabytes) available for allocating
                     new virtual disk extents.
 
 The functionality provided by these scripts is also available directly from
-Python functions in the XenoUtil module - you can use this functionality in
+Python functions in the xenctl.utils module - you can use this functionality in
 your own scripts.
 
 Populating VDs:
index 167615d758e36365508e3fcfc8f43065aa4da41b..a2b1906f71a44aa9dc79b19bdb080b29b67dd896 100644 (file)
@@ -312,10 +312,10 @@ provide access to privileged command operations:
 In this way you can see that the class 'xc' contains useful
 documentation for you to consult.
 
-A further module of useful routines (XenoUtil) is also installed:
+A further package of useful routines (xenctl) is also installed:
 
-# import XenoUtil
-# help(XenoUtil)
+# import xenctl.utils
+# help(xenctl.utils)
 
 You can use these modules to write your own custom scripts or you can
 customise the scripts supplied in the Xen distribution.
index 720b61417f7a31e0d5f0cfaf5120f24ba2390b3d..3bc0d81eaf18a3bc09011d153b5e75a7dae60c79 100644 (file)
@@ -23,6 +23,7 @@ all:
        $(MAKE) -C misc
        $(MAKE) -C examples
        $(MAKE) -C xentrace
+       $(MAKE) -C xenctl
        $(MAKE) -C xend
 
 install: all
@@ -31,6 +32,7 @@ install: all
        $(MAKE) -C misc install
        $(MAKE) -C examples install
        $(MAKE) -C xentrace install
+       $(MAKE) -C xenctl install
        $(MAKE) -C xend install
 
 clean:
@@ -40,5 +42,6 @@ clean:
        $(MAKE) -C examples clean
        $(MAKE) -C nsplitd clean
        $(MAKE) -C xentrace clean
+       $(MAKE) -C xenctl clean
        $(MAKE) -C xend clean
 
index 82041d7ad1b2b2d1bd82a49cac6cf3ccebf7288e..565b5c5c76e216af6ef194ab5a1256845cb15a0b 100644 (file)
@@ -5,7 +5,7 @@ This directory contains a set of example scripts for common Xen operations.
 For many operations you will either be able to use these scripts directly, or
 incorporate code from them into your own scripts.
 
-The Xc and XenoUtil Python modules provide an API for accessing all this
+The Xc and xenctl.utils Python modules provide an API for accessing all this
 functionality - and more - from your own Python programs.  These libraries may
 contain features for which there aren't yet example scripts written for...
 
index 8852094eeadb17f0f81d14fc3da74f3987689131..9f1a7874ab9645dd438fdbbbd4397348b114899d 100644 (file)
@@ -42,8 +42,8 @@ domain_name = "This is VM %d" % vmid
 # appropriately.
 
 #vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
-vfr_ipaddr  = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid),
-              XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+vfr_ipaddr  = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
+              xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
 
 
 # STEP 5a. Identify any physcial partitions or virtual disks you want the
@@ -72,8 +72,8 @@ vbd_expert = 0
 # You can use 'extrabit' to set the runlevel and custom environment
 # variables used by custom rc scripts (e.g. VMID=, usr= )
 
-netmask = XenoUtil.get_current_ipmask()
-gateway = XenoUtil.get_current_ipgw()
+netmask = xenctl.utils.get_current_ipmask()
+gateway = xenctl.utils.get_current_ipgw()
 nfsserv = '169.254.1.0'  
 
 cmdline_ip = "ip="+vfr_ipaddr[0]+":"+nfsserv+":"+gateway+":"+netmask+"::eth0:off"
index 4d6e90033f25b776ffe7698aea72dd24159d24ed..b8e9c0c928653847732e96dfa912bf475a8fad7a 100644 (file)
@@ -49,7 +49,7 @@ else:
 # appropriately.
 
 #vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
-#vfr_ipaddr  = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid)]
+#vfr_ipaddr  = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid)]
 vfr_ipaddr  = map(socket.gethostbyname,string.split(ip,','))
 
 
@@ -83,7 +83,7 @@ vbd_expert = 0
 # see if we have a local IP at all
 localip=''
 for i in vfr_ipaddr:
-    if XenoUtil.check_subnet(i,'169.254.0.0','255.255.0.0'):
+    if xenctl.utils.check_subnet(i,'169.254.0.0','255.255.0.0'):
        localip=i
        break
 
@@ -99,15 +99,15 @@ try:
     myip = vfr_ipaddr[0]
 
 except NameError:
-    netmask = XenoUtil.get_current_ipmask()
-    gateway = XenoUtil.get_current_ipgw()
+    netmask = xenctl.utils.get_current_ipmask()
+    gateway = xenctl.utils.get_current_ipgw()
 
 # if we haven't got an address, see if we have one that matches the LAN
 
 if not myip:
     if netmask and gateway:
        for i in vfr_ipaddr:
-           if XenoUtil.check_subnet(i,gateway,netmask): 
+           if xenctl.utils.check_subnet(i,gateway,netmask): 
                myip=i
                break
 
index 6050c8bc23510a8b3ce3c2fc0164b0ad8b3551c4..f3dcccbea3529f9ae035c9c13aaa8308a28b95bf 100644 (file)
@@ -57,10 +57,10 @@ except:
 
 #vfr_ipaddr = ["111.222.333.444","222.333.444.555"]
 try:
-    vfr_ipaddr = [ip, XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+    vfr_ipaddr = [ip, xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
 except:
-    vfr_ipaddr = [XenoUtil.add_offset_to_ip(XenoUtil.get_current_ipaddr(),vmid),
-                  XenoUtil.add_offset_to_ip('169.254.1.0',vmid),]
+    vfr_ipaddr = [xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
+                  xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),]
 
 
 # STEP 5a. Identify any physcial partitions or virtual disks you want the
@@ -89,8 +89,8 @@ vbd_expert = 0
 # You can use 'extrabit' to set the runlevel and custom environment
 # variables used by custom rc scripts (e.g. VMID=, usr= )
 
-netmask = XenoUtil.get_current_ipmask()
-gateway = XenoUtil.get_current_ipgw()
+netmask = xenctl.utils.get_current_ipmask()
+gateway = xenctl.utils.get_current_ipgw()
 try:
     nfsserv
 except:
index 638a509106f7792a46497d4a6c845eb02a56c337..0ca2c97ac2244caef50c9fcea37e9420d7c32972 100755 (executable)
@@ -184,8 +184,8 @@ elif cmd == 'vif_addip':
     ip  = sys.argv[4]
 
     # XXX This function should be moved to Xc once we sort out the VFR
-    import XenoUtil
-    XenoUtil.setup_vfr_rules_for_vif( dom, vif, ip )
+    import xenctl.utils
+    xenctl.utils.setup_vfr_rules_for_vif( dom, vif, ip )
 
 elif cmd == 'vif_setsched':
     if len(sys.argv) < 6:
@@ -211,9 +211,9 @@ elif cmd == 'vif_getsched':
 
 
 elif cmd == 'vbd_add':
-    import XenoUtil
+    import xenctl.utils
     
-    XenoUtil.VBD_EXPERT_LEVEL = 0 # sets the allowed level of potentially unsafe mappings
+    xenctl.utils.VBD_EXPERT_LEVEL = 0 # sets the allowed level of potentially unsafe mappings
 
     if len(sys.argv) < 6:
        usage()
@@ -227,17 +227,17 @@ elif cmd == 'vbd_add':
     if mode == 'rw' or mode == 'w':
        writeable = 1;
 
-    segments = XenoUtil.lookup_disk_uname(uname)
+    segments = xenctl.utils.lookup_disk_uname(uname)
 
     if not segments:
         print "Lookup Failed"
         sys.exit(1)
 
-    if XenoUtil.vd_extents_validate(segments,writeable) < 0:
+    if xenctl.utils.vd_extents_validate(segments,writeable) < 0:
        print "That mapping is too unsafe for the current VBD expertise level"
        sys.exit(1)
 
-    virt_dev = XenoUtil.blkdev_name_to_number(dev)
+    virt_dev = xenctl.utils.blkdev_name_to_number(dev)
 
     xc.vbd_create(dom,virt_dev,writeable)
 
@@ -248,14 +248,14 @@ elif cmd == 'vbd_add':
     print "Added disk/partition %s to domain %d as device %s (%x)" % (uname, dom, dev, virt_dev)
 
 elif cmd == 'vbd_remove':
-    import XenoUtil
+    import xenctl.utils
 
     if len(sys.argv) < 4:
        usage()
        sys.exit(1)
 
     dev = sys.argv[3]
-    virt_dev = XenoUtil.blkdev_name_to_number(dev)
+    virt_dev = xenctl.utils.blkdev_name_to_number(dev)
 
     if not xc.vbd_destroy(dom,virt_dev):
        print "Removed disk/partition attached as device %s (%x) in domain %d" % (dev, virt_dev, dom)
index 94f37a4494fa6d022a256e7c380b35baa29ca4f2..886dd31f0f93ab1a2f055dd1cb50642c6635e984 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
-import Xc, XenoUtil, string, sys, os, time, socket, getopt, signal, syslog
+import string, sys, os, time, socket, getopt, signal, syslog
+import Xc, xenctl.utils, xenctl.console_client
 
 config_dir  = '/etc/xc/'
 config_file = xc_config_file = config_dir + 'defaults'
@@ -29,6 +30,7 @@ Arguments to control the parsing of the defaults file:
 def extra_usage ():
     print >>sys.stderr,"""
 Arguments to override current config read from '%s':
+ -c               -- Turn into console terminal after domain is created
  -k image         -- Path to kernel image ['%s']
  -r ramdisk       -- Path to ramdisk (or empty) ['%s']
  -b builder_fn    -- Function to use to build domain ['%s']
@@ -86,12 +88,13 @@ mem_size=0; domain_name=''; vfr_ipaddr=[];
 vbd_expert=0; auto_restart=False;
 vbd_list = []; cmdline_ip = ''; cmdline_root=''; cmdline_extra=''
 pci_device_list = []
+auto_console = False
 
 ##### Determine location of defautls file
 #####
 
 try:
-    opts, args = getopt.getopt(sys.argv[1:], "h?nqf:D:k:r:b:m:N:a:e:d:i:I:R:E:L:" )
+    opts, args = getopt.getopt(sys.argv[1:], "h?nqcf:D:k:r:b:m:N:a:e:d:i:I:R:E:L:" )
 
     for opt in opts:
        if opt[0] == '-f': config_file= opt[1]
@@ -160,6 +163,7 @@ for opt in opts:
     if opt[0] == '-R': cmdline_root = opt[1]
     if opt[0] == '-E': cmdline_extra = opt[1]
     if opt[0] == '-i': x_vfr_ipaddr.append(opt[1])
+    if opt[0] == '-c': auto_console = True
     if opt[0] == '-d':
        try:
            vv = string.split(opt[1],';')           
@@ -238,7 +242,7 @@ def make_domain():
             sys.exit()
 
     cmsg = 'new_control_interface(dom='+str(id)+')'
-    xend_response = XenoUtil.xend_control_message(cmsg)
+    xend_response = xenctl.utils.xend_control_message(cmsg)
     if not xend_response['success']:
         print "Error creating initial event channel"
         print "Error type: " + xend_response['error_type']
@@ -251,12 +255,12 @@ def make_domain():
     # setup the virtual block devices
 
     # set the expertise level appropriately
-    XenoUtil.VBD_EXPERT_MODE = vbd_expert
+    xenctl.utils.VBD_EXPERT_MODE = vbd_expert
     
     for ( uname, virt_name, rw ) in vbd_list:
-       virt_dev = XenoUtil.blkdev_name_to_number( virt_name )
+       virt_dev = xenctl.utils.blkdev_name_to_number( virt_name )
 
-       segments = XenoUtil.lookup_disk_uname( uname )
+       segments = xenctl.utils.lookup_disk_uname( uname )
        if not segments:
            print "Error looking up %s\n" % uname
            xc.domain_destroy ( dom=id )
@@ -264,7 +268,7 @@ def make_domain():
 
         # check that setting up this VBD won't violate the sharing
         # allowed by the current VBD expertise level
-        if XenoUtil.vd_extents_validate(segments, rw=='w' or rw=='rw') < 0:
+        if xenctl.utils.vd_extents_validate(segments, rw=='w' or rw=='rw') < 0:
             xc.domain_destroy( dom = id )
             sys.exit()
             
@@ -282,7 +286,7 @@ def make_domain():
 
     # setup virtual firewall rules for all aliases
     for ip in vfr_ipaddr:
-       XenoUtil.setup_vfr_rules_for_vif( id, 0, ip )
+       xenctl.utils.setup_vfr_rules_for_vif( id, 0, ip )
 
     # check for physical device access
     for (pci_bus, pci_dev, pci_func) in pci_device_list:
@@ -328,6 +332,9 @@ def death_handler(dummy1,dummy2):
 (current_id, current_port) = make_domain()
 output("VM started in domain %d. Console I/O available on TCP port %d." % (current_id,current_port))
 
+if auto_console:
+    xenctl.console_client.connect('127.0.0.1',int(current_port))
+
 # if the auto_restart flag is set then keep polling to see if the domain is
 # alive - restart if it is not by calling make_domain() again (it's necessary
 # to update the id variable, since the new domain may have a new ID)
index 95e316c7410ad8898d5dd42de9f96752c9408fd2..9184d357bdca1bca22fd378230b02ad0c29108f8 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-import XenoUtil, sys, re, string
+import xenctl.utils, sys, re, string
 
 def usage():
 
@@ -50,7 +50,7 @@ if cmd == 'initialise':
     print "Device: " + dev
     print "Extent size: " + str(extent_size) + "MB"
 
-    rc = XenoUtil.vd_format(dev, extent_size)
+    rc = xenctl.utils.vd_format(dev, extent_size)
 
 elif cmd == 'create':
  
@@ -63,7 +63,7 @@ elif cmd == 'create':
     print "Size: %d" % size
     print "Expiry time (seconds from now): %d" % expiry_time
 
-    src = XenoUtil.vd_create(size, expiry_time)
+    src = xenctl.utils.vd_create(size, expiry_time)
 
 elif cmd == 'enlarge':
 
@@ -71,7 +71,7 @@ elif cmd == 'enlarge':
 
     extra_size = int(sys.argv[3])
 
-    rc = XenoUtil.vd_enlarge(id, extra_size)
+    rc = xenctl.utils.vd_enlarge(id, extra_size)
 
 elif cmd == 'delete':
 
@@ -79,7 +79,7 @@ elif cmd == 'delete':
 
     print "Deleting a virtual disk with ID: " + id
 
-    rc = XenoUtil.vd_delete(id)
+    rc = xenctl.utils.vd_delete(id)
 
 elif cmd == 'import':
 
@@ -90,7 +90,7 @@ elif cmd == 'import':
 
     print "Allocate new virtual disk and populate from file : %s" % file
 
-    print XenoUtil.vd_read_from_file(file, expiry_time)
+    print xenctl.utils.vd_read_from_file(file, expiry_time)
 
 elif cmd == 'export':
 
@@ -99,7 +99,7 @@ elif cmd == 'export':
 
     print "Dump contents of virtual disk to file : %s" % file
 
-    rc = XenoUtil.vd_cp_to_file(id, file )
+    rc = xenctl.utils.vd_cp_to_file(id, file )
 
 elif cmd == 'setexpiry':
 
@@ -112,19 +112,19 @@ elif cmd == 'setexpiry':
     print "Id: " + id
     print "Expiry time (seconds from now [or 0]): " + str(expiry_time)
 
-    rc = XenoUtil.vd_refresh(id, expiry_time)
+    rc = xenctl.utils.vd_refresh(id, expiry_time)
 
 elif cmd == 'list':
     print 'ID    Size(MB)      Expiry'
 
-    for vbd in XenoUtil.vd_list():
+    for vbd in xenctl.utils.vd_list():
         vbd['size_mb'] = vbd['size'] / 2048
         vbd['expiry'] = (vbd['expires'] and vbd['expiry_time']) or 'never'
         print '%(vdisk_id)-4s  %(size_mb)-12d  %(expiry)s' % vbd
 
 elif cmd == 'freespace':
 
-    print XenoUtil.vd_freespace()
+    print xenctl.utils.vd_freespace()
 
 elif cmd == 'undelete':
 
@@ -133,7 +133,7 @@ elif cmd == 'undelete':
     if len(sys.argv) > 3:
        expiry_time = int(sys.argv[3])
    
-    if XenoUtil.vd_undelete(id, expiry_time):
+    if xenctl.utils.vd_undelete(id, expiry_time):
        print "Undelete operation failed for virtual disk: " + id
     else:
        print "Undelete operation succeeded for virtual disk: " + id
diff --git a/tools/xc/py/XenoUtil.py b/tools/xc/py/XenoUtil.py
deleted file mode 100644 (file)
index 520f585..0000000
+++ /dev/null
@@ -1,1014 +0,0 @@
-import os, re, socket, string, sys, tempfile
-
-##### Module variables
-
-"""Location of the Virtual Disk management database.
-   defaults to /var/db/xen_vdisks.sqlite
-"""
-VD_DB_FILE = "/var/db/xen_vdisks.sqlite"
-
-"""VBD expertise level - determines the strictness of the sanity checking.
-  This mode determines the level of complaints when disk sharing occurs
-  through the current VBD mappings.
-   0 - only allow shared mappings if both domains have r/o access (always OK)
-   1 - also allow sharing with one dom r/w and the other r/o
-   2 - allow sharing with both doms r/w
-"""
-VBD_EXPERT_MODE = 0
-
-##### Module initialisation
-
-try:
-    # try to import sqlite (not everyone will have it installed)
-    import sqlite
-except ImportError:
-    # on failure, just catch the error, don't do anything
-    pass
-
-
-##### Networking-related functions
-
-def get_current_ipaddr(dev='eth0'):
-    """Return a string containing the primary IP address for the given
-    network interface (default 'eth0').
-    """
-    fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
-    lines = fd.readlines()
-    for line in lines:
-        m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
-                       line )
-        if m:
-            return m.group(1)
-    return None
-
-def get_current_ipmask(dev='eth0'):
-    """Return a string containing the primary IP netmask for the given
-    network interface (default 'eth0').
-    """
-    fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
-    lines = fd.readlines()
-    for line in lines:
-        m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
-                       line )
-        if m:
-            return m.group(1)
-    return None
-
-def get_current_ipgw(dev='eth0'):
-    """Return a string containing the IP gateway for the given
-    network interface (default 'eth0').
-    """
-    fd = os.popen( '/sbin/route -n' )
-    lines = fd.readlines()
-    for line in lines:
-        m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
-                       '\s+\S+\s+\S*G.*' + dev + '.*', line )
-        if m:
-            return m.group(1)
-    return None
-
-def setup_vfr_rules_for_vif(dom,vif,addr):
-    """Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
-    is expressed as a textual dotted quad, and set up appropriate routing
-    rules in Xen. No return value.
-    """
-    fd = os.open( '/proc/xen/vfr', os.O_WRONLY )
-    if ( re.search( '169\.254', addr) ):
-        os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
-                  ' srcaddrmask=255.255.255.255' +
-                  ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
-                  ' dstdom=0 dstidx=0 proto=any\n' )
-    else:
-        os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
-                  ' srcaddrmask=255.255.255.255' +
-                  ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
-                  ' dst=PHYS proto=any\n' )
-    os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
-              ' dstaddrmask=255.255.255.255' +
-              ' src=ANY' +
-              ' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
-              ' proto=any\n' )
-    os.close( fd )
-    return None
-
-def add_offset_to_ip( ip, off ):
-    l = string.split(ip,'.')
-    a = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
-         (string.atoi(l[2])<<8)  | string.atoi(l[3]) ) + off
-    
-    return '%d.%d.%d.%d' % ( ((a>>24)&0xff), ((a>>16)&0xff),
-                            ((a>>8)&0xff), (a&0xff) )
-
-def check_subnet( ip, network, netmask ):
-    l = string.split(ip,'.')
-    n_ip = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
-          (string.atoi(l[2])<<8)  | string.atoi(l[3]) ) 
-
-    l = string.split(network,'.')
-    n_net = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
-          (string.atoi(l[2])<<8)  | string.atoi(l[3]) )
-
-    l = string.split(netmask,'.')
-    n_mask = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
-          (string.atoi(l[2])<<8)  | string.atoi(l[3]) )
-    
-    return (n_ip&n_mask)==(n_net&n_mask)
-
-
-##### VBD-related Functions
-
-def blkdev_name_to_number(name):
-    """Take the given textual block-device name (e.g., '/dev/sda1',
-    'hda') and return the device number used by the OS. """
-
-    if not re.match( '/dev/', name ):
-        name = '/dev/' + name
-        
-    return os.stat(name).st_rdev
-
-# lookup_blkdev_partn_info( '/dev/sda3' )
-def lookup_raw_partn(partition):
-    """Take the given block-device name (e.g., '/dev/sda1', 'hda')
-    and return a dictionary { device, start_sector,
-    nr_sectors, type }
-        device:       Device number of the given partition
-        start_sector: Index of first sector of the partition
-        nr_sectors:   Number of sectors comprising this partition
-        type:         'Disk' or identifying name for partition type
-    """
-
-    if not re.match( '/dev/', partition ):
-        partition = '/dev/' + partition
-
-    drive = re.split( '[0-9]', partition )[0]
-
-    if drive == partition:
-        fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
-        line = fd.readline()
-        if line:
-            return [ { 'device' : blkdev_name_to_number(drive),
-                       'start_sector' : long(0),
-                       'nr_sectors' : long(line) * 2,
-                       'type' : 'Disk' } ]
-        return None
-
-    # determine position on disk
-    fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
-
-    #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
-    lines = fd.readlines()
-    for line in lines:
-        m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
-                       'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
-        if m:
-            return [ { 'device' : blkdev_name_to_number(drive),
-                       'start_sector' : long(m.group(1)),
-                       'nr_sectors' : long(m.group(2)),
-                       'type' : m.group(3) } ]
-    
-    return None
-
-def lookup_disk_uname( uname ):
-    """Lookup a list of segments for either a physical or a virtual device.
-    uname [string]:  name of the device in the format \'vd:id\' for a virtual
-                     disk, or \'phy:dev\' for a physical device
-    returns [list of dicts]: list of extents that make up the named device
-    """
-    ( type, d_name ) = string.split( uname, ':' )
-
-    if type == "phy":
-        segments = lookup_raw_partn( d_name )
-    elif type == "vd":
-       segments = vd_lookup( d_name )
-
-    return segments
-
-
-##### Management of the Xen control daemon
-##### (c) Keir Fraser, University of Cambridge
-
-def xend_control_message( message ):
-    """Takes a textual control message and sends it to the 'xend' Xen
-    control daemon. Returns a dictionary containing the daemon's multi-part
-    response."""
-    tmpdir = tempfile.mkdtemp()
-    try:
-        ctl = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
-        ctl.bind(tmpdir+'/sock')
-        ctl.sendto(message, '/var/run/xend/management_sock')
-        data, addr = ctl.recvfrom(2048)
-        ctl.close()
-    finally:
-        if os.path.exists(tmpdir+'/sock'):
-            os.unlink(tmpdir+'/sock')
-        if os.path.exists(tmpdir):
-            os.rmdir(tmpdir)    
-    return eval(data)
-
-
-##### VD Management-related functions
-
-##### By Mark Williamson, <mark.a.williamson@intel.com>
-##### (C) Intel Research Cambridge
-
-# TODO:
-#
-# Plenty of room for enhancement to this functionality (contributions
-# welcome - and then you get to have your name in the source ;-)...
-#
-# vd_unformat() : want facilities to unallocate virtual disk
-# partitions, possibly migrating virtual disks of them, with checks to see if
-# it's safe and options to force it anyway
-#
-# vd_create() : should have an optional argument specifying a physical
-# disk preference - useful to allocate for guest doms to do RAID
-#
-# vd_undelete() : add ability to "best effort" undelete as much of a
-# vdisk as is left in the case that some of it has already been
-# reallocated.  Some people might still be able to recover some of
-# their data this way, even if some of the disk has disappeared.
-#
-# It'd be nice if we could wipe virtual disks for security purposes -
-# should be easy to do this using dev if=/dev/{zero,random} on each
-# extent in turn.  There could be another optional flag to vd_create
-# in order to allow this.
-#
-# Error codes could be more expressive - i.e. actually tell why the
-# error occurred rather than "it broke".  Currently the code avoids
-# using exceptions to make control scripting simpler and more
-# accessible to beginners - therefore probably should just use more
-# return codes.
-#
-# Enhancements / additions to the example scripts are also welcome:
-# some people will interact with this code mostly through those
-# scripts.
-#
-# More documentation of how this stuff should be used is always nice -
-# if you have a novel configuration that you feel isn't discussed
-# enough in the HOWTO (which is currently a work in progress), feel
-# free to contribute a walkthrough, or something more substantial.
-#
-
-
-def __vd_no_database():
-    """Called when no database found - exits with an error
-    """
-    print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
-    sys.exit(1)
-
-
-def vd_format(partition, extent_size_mb):
-    """Format a partition or drive for use a virtual disk storage.
-    partition [string]: device file representing the partition
-    extent_size_mb [string]: extent size in megabytes to use on this disk
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        vd_init_db(VD_DB_FILE)
-    
-    if not re.match( '/dev/', partition ):
-        partition = '/dev/' + partition
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("select * from vdisk_part where partition = \'"
-               + partition + "\'")
-    row = cu.fetchone()
-
-    extent_size = extent_size_mb * 2048 # convert megabytes to sectors
-    
-    if not row:
-        part_info = lookup_raw_partn(partition)[0]
-        
-        cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
-                   "VALUES ( \'" + partition + "\', "
-                   + str(blkdev_name_to_number(partition))
-                   + ", " + str(extent_size) + ")")
-
-
-        cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
-                   + "WHERE vdisk_id = 0")
-        
-        max_id, = cu.fetchone()
-
-        if max_id != None:
-            new_id = max_id + 1
-        else:
-            new_id = 0
-
-        num_extents = part_info['nr_sectors'] / extent_size
-
-        for i in range(num_extents):
-            sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
-                                              part_id, part_extent_no)
-                    VALUES ("""+ str(new_id + i) + ", 0, "\
-                               + str(blkdev_name_to_number(partition))\
-                               + ", " + str(num_extents - (i + 1)) + ")"
-            cu.execute(sql)
-
-    cx.commit()
-    cx.close()
-    return 0
-
-
-def vd_create(size_mb, expiry):
-    """Create a new virtual disk.
-    size_mb [int]: size in megabytes for the new virtual disk
-    expiry [int]: expiry time in seconds from now
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    size = size_mb * 2048
-
-    cu.execute("SELECT max(vdisk_id) FROM vdisks")
-    max_id, = cu.fetchone()
-    new_id = int(max_id) + 1
-
-    # fetch a list of extents from the expired disks, along with information
-    # about their size
-    cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
-                         vdisk_extents.part_id, extent_size
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                                  NATURAL JOIN vdisk_part
-                  WHERE expires AND expiry_time <= datetime('now')
-                  ORDER BY expiry_time ASC, vdisk_extent_no DESC
-               """)  # aims to reuse the last extents
-                     # from the longest-expired disks first
-
-    allocated = 0
-
-    if expiry:
-        expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
-        expires = 1
-    else:
-        expiry_ts = "NULL"
-        expires = 0
-
-    # we'll use this to build the SQL statement we want
-    building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
-                   +" VALUES ("+str(new_id)+", "+str(size)+ ", "              \
-                   + str(expires) + ", " + expiry_ts + "); "
-
-    counter = 0
-
-    while allocated < size:
-        row = cu.fetchone()
-        if not row:
-            print "ran out of space, having allocated %d meg of %d" % (allocated, size)
-            cx.close()
-            return -1
-        
-
-        (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
-        allocated += extent_size
-        building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
-                        + ", " + "vdisk_extent_no = " + str(counter)         \
-                        + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
-                        + " AND vdisk_id = " + str(vdisk_id) + "; "
-
-        counter += 1
-        
-
-    # this will execute the SQL query we build to store details of the new
-    # virtual disk and allocate space to it print building_sql
-    cu.execute(building_sql)
-    
-    cx.commit()
-    cx.close()
-    return str(new_id)
-
-
-def vd_lookup(id):
-    """Lookup a Virtual Disk by ID.
-    id [string]: a virtual disk identifier
-    Returns [list of dicts]: a list of extents as dicts, containing fields:
-                             device : Linux device number of host disk
-                             start_sector : within the device
-                             nr_sectors : size of this extent
-                             type : set to \'VD Extent\'
-                             
-                             part_device : Linux device no of host partition
-                             part_start_sector : within the partition
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("-- types int")
-    cu.execute("""SELECT COUNT(*)
-                  FROM vdisks
-                  WHERE (expiry_time > datetime('now') OR NOT expires)
-                              AND vdisk_id = """ + id)
-    count, = cu.fetchone()
-
-    if not count:
-        cx.close()
-        return None
-
-    cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
-    real_size, = cu.fetchone()
-  
-    # This query tells PySQLite how to convert the data returned from the
-    # following query - the use of the multiplication confuses it otherwise ;-)
-    # This row is significant to PySQLite but is syntactically an SQL comment.
-
-    cu.execute("-- types str, int, int, int")
-
-    # This SQL statement is designed so that when the results are fetched they
-    # will be in the right format to return immediately.
-    cu.execute("""SELECT partition, vdisk_part.part_id,
-                         round(part_extent_no * extent_size) as start,
-                         extent_size
-                         
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                             NATURAL JOIN vdisk_part
-                                                
-                  WHERE vdisk_extents.vdisk_id = """ + id
-               + " ORDER BY vdisk_extents.vdisk_extent_no ASC"
-               )
-
-    extent_tuples = cu.fetchall()
-
-    # use this function to map the results from the database into a dict
-    # list of extents, for consistency with the rest of the code
-    def transform ((partition, part_device, part_offset, nr_sectors)):
-        return {
-                 # the disk device this extent is on - for passing to Xen
-                 'device' : lookup_raw_partn(partition)[0]['device'],
-                 # the offset of this extent within the disk - for passing to Xen
-                 'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
-                 # extent size, in sectors
-                 'nr_sectors' : nr_sectors,
-                 # partition device this extent is on (useful to know for XenoUtil fns)
-                 'part_device' : part_device,
-                 # start sector within this partition (useful to know for XenoUtil fns)
-                 'part_start_sector' : part_offset,
-                 # type of this extent - handy to know
-                 'type' : 'VD Extent' }
-
-    cx.commit()
-    cx.close()
-
-    extent_dicts = map(transform, extent_tuples)
-
-    # calculate the over-allocation in sectors (happens because
-    # we allocate whole extents)
-    allocated_size = 0
-    for i in extent_dicts:
-        allocated_size += i['nr_sectors']
-
-    over_allocation = allocated_size - real_size
-
-    # trim down the last extent's length so the resulting VBD will be the
-    # size requested, rather than being rounded up to the nearest extent
-    extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
-
-    return extent_dicts
-
-
-def vd_enlarge(vdisk_id, extra_size_mb):
-    """Create a new virtual disk.
-    vdisk_id [string]   :    ID of the virtual disk to enlarge
-    extra_size_mb  [int]:    size in megabytes to increase the allocation by
-    returns  [int]      :    0 on success, otherwise non-zero
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    extra_size = extra_size_mb * 2048
-
-    cu.execute("-- types int")
-    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
-               + " AND (expiry_time > datetime('now') OR NOT expires)")
-    count, = cu.fetchone()
-
-    if not count: # no such vdisk
-        cx.close()
-        return -1
-
-    cu.execute("-- types int")
-    cu.execute("""SELECT SUM(extent_size)
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                         NATURAL JOIN vdisk_part
-                  WHERE vdisks.vdisk_id = """ + vdisk_id)
-
-    real_size, = cu.fetchone() # get the true allocated size
-
-    cu.execute("-- types int")
-    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
-
-    old_size, = cu.fetchone()
-
-
-    cu.execute("--- types int")
-    cu.execute("""SELECT MAX(vdisk_extent_no)
-                  FROM vdisk_extents
-                  WHERE vdisk_id = """ + vdisk_id)
-
-    counter = cu.fetchone()[0] + 1 # this stores the extent numbers
-
-
-    # because of the extent-based allocation, the VD may already have more
-    # allocated space than they asked for.  Find out how much we really
-    # need to add.
-    add_size = extra_size + old_size - real_size
-
-    # fetch a list of extents from the expired disks, along with information
-    # about their size
-    cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
-                         vdisk_extents.part_id, extent_size
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                                  NATURAL JOIN vdisk_part
-                  WHERE expires AND expiry_time <= datetime('now')
-                  ORDER BY expiry_time ASC, vdisk_extent_no DESC
-               """)  # aims to reuse the last extents
-                     # from the longest-expired disks first
-
-    allocated = 0
-
-    building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
-                   + " WHERE vdisk_id = " + vdisk_id + "; "
-
-    while allocated < add_size:
-        row = cu.fetchone()
-        if not row:
-            cx.close()
-            return -1
-
-        (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
-        allocated += extent_size
-        building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id    \
-                        + ", " + "vdisk_extent_no = " + str(counter)         \
-                        + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
-                        + " AND vdisk_id = " + str(dead_vd_id) + "; "
-
-        counter += 1
-        
-
-    # this will execute the SQL query we build to store details of the new
-    # virtual disk and allocate space to it print building_sql
-    cu.execute(building_sql)
-    
-    cx.commit()
-    cx.close()
-    return 0
-
-
-def vd_undelete(vdisk_id, expiry_time):
-    """Create a new virtual disk.
-    vdisk_id      [int]: size in megabytes for the new virtual disk
-    expiry_time   [int]: expiry time, in seconds from now
-    returns       [int]: zero on success, non-zero on failure
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-
-    if vdisk_id == '0': #  undeleting vdisk 0 isn't sane!
-        return -1
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("-- types int")
-    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
-    count, = cu.fetchone()
-
-    if not count:
-        cx.close()
-        return -1
-
-    cu.execute("-- types int")
-    cu.execute("""SELECT SUM(extent_size)
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                         NATURAL JOIN vdisk_part
-                  WHERE vdisks.vdisk_id = """ + vdisk_id)
-
-    real_size, = cu.fetchone() # get the true allocated size
-
-
-    cu.execute("-- types int")
-    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
-
-    old_size, = cu.fetchone()
-
-    if real_size < old_size:
-        cx.close()
-        return -1
-
-    if expiry_time == 0:
-        expires = '0'
-    else:
-        expires = '1'
-
-    # this will execute the SQL query we build to store details of the new
-    # virtual disk and allocate space to it print building_sql
-    cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
-               + str(expiry_time) + " seconds'), expires = " + expires
-               + " WHERE vdisk_id = " + vdisk_id)
-    
-    cx.commit()
-    cx.close()
-    return 0
-
-
-
-
-def vd_list():
-    """Lists all the virtual disks registered in the system.
-    returns [list of dicts]
-    """
-    
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("""SELECT vdisk_id, size, expires, expiry_time
-                  FROM vdisks
-                  WHERE (NOT expires) OR expiry_time > datetime('now')
-               """)
-
-    ret = cu.fetchall()
-
-    cx.close()
-
-    def makedicts((vdisk_id, size, expires, expiry_time)):
-        return { 'vdisk_id' : str(vdisk_id), 'size': size,
-                 'expires' : expires, 'expiry_time' : expiry_time }
-
-    return map(makedicts, ret)
-
-
-def vd_refresh(id, expiry):
-    """Change the expiry time of a virtual disk.
-    id [string]  : a virtual disk identifier
-    expiry [int] : expiry time in seconds from now (0 = never expire)
-    returns [int]: zero on success, non-zero on failure
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-    
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("-- types int")
-    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
-               + " AND (expiry_time > datetime('now') OR NOT expires)")
-    count, = cu.fetchone()
-
-    if not count:
-        cx.close()
-        return -1
-
-    if expiry:
-        expires = 1
-        expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
-    else:
-        expires = 0
-        expiry_ts = "NULL"
-
-    cu.execute("UPDATE vdisks SET expires = " + str(expires)
-               + ", expiry_time = " + expiry_ts
-               + " WHERE (expiry_time > datetime('now') OR NOT expires)"
-               + " AND vdisk_id = " + id)
-
-    cx.commit()
-    cx.close()
-    
-    return 0
-
-
-def vd_delete(id):
-    """Deletes a Virtual Disk, making its extents available for future VDs.
-       id [string]   : identifier for the virtual disk to delete
-       returns [int] : 0 on success, -1 on failure (VD not found
-                       or already deleted)
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-    
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("-- types int")
-    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
-               + " AND (expiry_time > datetime('now') OR NOT expires)")
-    count, = cu.fetchone()
-
-    if not count:
-        cx.close()
-        return -1
-
-    cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
-               + " WHERE vdisk_id = " + id)
-
-    cx.commit()
-    cx.close()
-    
-    return 0
-
-
-def vd_freespace():
-    """Returns the amount of free space available for new virtual disks, in MB
-    returns [int] : free space for VDs in MB
-    """
-
-    if not os.path.isfile(VD_DB_FILE):
-        __vd_no_database()
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("-- types int")
-
-    cu.execute("""SELECT SUM(extent_size)
-                  FROM vdisks NATURAL JOIN vdisk_extents
-                                           NATURAL JOIN vdisk_part
-                  WHERE expiry_time <= datetime('now') AND expires""")
-
-    sum, = cu.fetchone()
-
-    cx.close()
-
-    return sum / 2048
-
-
-def vd_init_db(path):
-    """Initialise the VD SQLite database
-    path [string]: path to the SQLite database file
-    """
-
-    cx = sqlite.connect(path)
-    cu = cx.cursor()
-
-    cu.execute(
-        """CREATE TABLE vdisk_extents
-                           ( vdisk_extent_no INT,
-                             vdisk_id INT,
-                             part_id INT,
-                             part_extent_no INT )
-        """)
-
-    cu.execute(
-        """CREATE TABLE vdisk_part
-                           ( part_id INT,
-                             partition VARCHAR,
-                             extent_size INT )
-        """)
-
-    cu.execute(
-        """CREATE TABLE vdisks
-                           ( vdisk_id INT,
-                             size INT,
-                             expires BOOLEAN,
-                             expiry_time TIMESTAMP )
-        """)
-
-
-    cu.execute(
-        """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
-                       VALUES ( 0,        0,    1,       datetime('now') )
-        """)
-
-    cx.commit()
-    cx.close()
-
-    VD_DB_FILE = path
-
-
-
-def vd_cp_to_file(vdisk_id,filename):
-    """Writes the contents of a specified vdisk out into a disk file, leaving
-    the original copy in the virtual disk pool."""
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    extents = vd_lookup(vdisk_id)
-
-    if not extents:
-        return -1
-    
-    file_idx = 0 # index into source file, in sectors
-
-    for i in extents:
-        cu.execute("""SELECT partition, extent_size FROM vdisk_part
-                      WHERE part_id =  """ + str(i['part_device']))
-
-        (partition, extent_size) = cu.fetchone()
-
-        os.system("dd bs=1b if=" + partition + " of=" + filename
-                  + " skip=" + str(i['part_start_sector'])
-                  + " seek=" + str(file_idx)
-                  + " count=" + str(i['nr_sectors'])
-                  + " > /dev/null")
-
-        file_idx += i['nr_sectors']
-
-    cx.close()
-
-    return 0 # should return -1 if something breaks
-    
-
-def vd_mv_to_file(vdisk_id,filename):
-    """Writes a vdisk out into a disk file and frees the space originally
-    taken within the virtual disk pool.
-    vdisk_id [string]: ID of the vdisk to write out
-    filename [string]: file to write vdisk contents out to
-    returns [int]: zero on success, nonzero on failure
-    """
-
-    if vd_cp_to_file(vdisk_id,filename):
-        return -1
-
-    if vd_delete(vdisk_id):
-        return -1
-
-    return 0
-
-
-def vd_read_from_file(filename,expiry):
-    """Reads the contents of a file directly into a vdisk, which is
-    automatically allocated to fit.
-    filename [string]: file to read disk contents from
-    returns [string] : vdisk ID for the destination vdisk
-    """
-
-    size_bytes = os.stat(filename).st_size
-
-    (size_mb,leftover) =  divmod(size_bytes,1048580) # size in megabytes
-    if leftover > 0: size_mb += 1 # round up if not an exact number of MB
-
-    vdisk_id = vd_create(size_mb, expiry)
-
-    if vdisk_id < 0:
-        return -1
-
-    cx = sqlite.connect(VD_DB_FILE)
-    cu = cx.cursor()
-
-    cu.execute("""SELECT partition, extent_size, part_extent_no
-                  FROM vdisk_part NATURAL JOIN vdisk_extents
-                  WHERE vdisk_id =  """ + vdisk_id + """
-                  ORDER BY vdisk_extent_no ASC""")
-
-    extents = cu.fetchall()
-
-    size_sectors = size_mb * 2048 # for feeding to dd
-
-    file_idx = 0 # index into source file, in sectors
-
-    def write_extent_to_vd((partition, extent_size, part_extent_no),
-                           file_idx, filename):
-        """Write an extent out to disk and update file_idx"""
-
-        os.system("dd bs=512 if=" + filename + " of=" + partition
-                  + " skip=" + str(file_idx)
-                  + " seek=" + str(part_extent_no * extent_size)
-                  + " count=" + str(min(extent_size, size_sectors - file_idx))
-                  + " > /dev/null")
-
-        return extent_size
-
-    for i in extents:
-        file_idx += write_extent_to_vd(i, file_idx, filename)
-
-    cx.close()
-
-    return vdisk_id
-    
-
-
-
-def vd_extents_validate(new_extents,new_writeable):
-    """Validate the extents against the existing extents.
-    Complains if the list supplied clashes against the extents that
-    are already in use in the system.
-    new_extents [list of dicts]: list of new extents, as dicts
-    new_writeable [int]: 1 if they are to be writeable, 0 otherwise
-    returns [int]: either the expertise level of the mapping if it doesn't
-                   exceed VBD_EXPERT_MODE or -1 if it does (error)
-    """
-
-    import Xc # this is only needed in this function
-
-    xc = Xc.new()
-
-    ##### Probe for explicitly created virtual disks and build a list
-    ##### of extents for comparison with the ones that are being added
-
-    probe = xc.vbd_probe()
-
-    old_extents = [] # this will hold a list of all existing extents and
-                     # their writeable status, as a list of (device,
-                     # start, size, writeable?) tuples
-
-    for vbd in probe:
-        this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
-        for vbd_ext in this_vbd_extents:
-            vbd_ext['writeable'] = vbd['writeable']
-            old_extents.append(vbd_ext)
-            
-    ##### Now scan /proc/mounts for compile a list of extents corresponding to
-    ##### any devices mounted in DOM0.  This list is added on to old_extents
-
-    regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
-    fd = open('/proc/mounts', "r")
-
-    while True:
-        line = fd.readline()
-        if not line: # if we've run out of lines then stop reading
-            break
-        
-        m = regexp.match(line)
-
-        # if the regexp didn't match then it's probably a line we don't
-        # care about - skip to next line
-        if not m:
-            continue
-
-        # lookup the device
-        ext_list = lookup_raw_partn(m.group(1))
-
-        # if lookup failed, skip to next mounted device
-        if not ext_list:
-            continue
-
-        # set a writeable flag as appropriate
-        for ext in ext_list:
-            ext['writeable'] = m.group(2) == 'rw'
-
-        # now we've got here, the contents of ext_list are in a
-        # suitable format to be added onto the old_extents list, ready
-        # for checking against the new extents
-
-        old_extents.extend(ext_list)
-
-    fd.close() # close /proc/mounts
-
-    ##### By this point, old_extents contains a list of extents, in
-    ##### dictionary format corresponding to every extent of physical
-    ##### disk that's either part of an explicitly created VBD, or is
-    ##### mounted under DOM0.  We now check these extents against the
-    ##### proposed additions in new_extents, to see if a conflict will
-    ##### happen if they are added with write status new_writeable
-
-    level = 0 # this'll accumulate the max warning level
-
-    # Search for clashes between the new extents and the old ones
-    # Takes time O(len(new_extents) * len(old_extents))
-    for new_ext in new_extents:
-        for old_ext in old_extents:
-            if(new_ext['device'] == old_ext['device']):
-
-                new_ext_start = new_ext['start_sector']
-                new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
-                
-                old_ext_start = old_ext['start_sector']
-                old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
-                
-                if((old_ext_start <= new_ext_start <= old_ext_end) or
-                   (old_ext_start <= new_ext_end <= old_ext_end)):
-                    if (not old_ext['writeable']) and new_writeable:
-                        level = max(1,level)
-                    elif old_ext['writeable'] and (not new_writeable):
-                        level = max(1,level)
-                    elif old_ext['writeable'] and new_writeable:
-                        level = max(2,level)
-
-
-    ##### level now holds the warning level incurred by the current
-    ##### VBD setup and we complain appropriately to the user
-
-
-    if level == 1:
-        print >> sys.stderr, """Warning: one or more hard disk extents
-         writeable by one domain are also readable by another."""
-    elif level == 2:
-        print >> sys.stderr, """Warning: one or more hard disk extents are
-         writeable by two or more domains simultaneously."""
-
-    if level > VBD_EXPERT_MODE:
-        print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
-        at the current safety level (%d).""" % VBD_EXPERT_MODE
-        level = -1
-
-    return level
-
index 6d8f4f83f01fee14b0d19727060358018b9075ce..a49971ab9f86e94f8b3455bc0b8c2ce89ca15b73 100644 (file)
@@ -8,5 +8,3 @@ module = Extension("Xc",
                    sources              = ["Xc.c"])
 
 setup(name = "Xc", version = "1.0", ext_modules = [module])
-
-setup(name = "XenoUtil", version = "1.0", py_modules = ["XenoUtil"])
diff --git a/tools/xenctl/Makefile b/tools/xenctl/Makefile
new file mode 100644 (file)
index 0000000..e3583f5
--- /dev/null
@@ -0,0 +1,10 @@
+
+all:
+       python setup.py build
+
+install: all
+       if [ "$(prefix)" = "" ]; then python setup.py install; \
+       else python setup.py install --home="$(prefix)"; fi
+
+clean:
+       rm -rf build *.pyc *.pyo *.o *.a *~
diff --git a/tools/xenctl/lib/__init__.py b/tools/xenctl/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/xenctl/lib/console_client.py b/tools/xenctl/lib/console_client.py
new file mode 100644 (file)
index 0000000..74fc978
--- /dev/null
@@ -0,0 +1,60 @@
+
+##############################################
+# Console client for Xen guest OSes
+# Copyright (c) 2004, K A Fraser
+##############################################
+
+import errno, os, signal, socket, struct, sys, termios
+
+def __child_death(signum, frame):
+    global stop
+    stop = True
+
+def __recv_from_sock(sock):
+    global stop
+    stop = False
+    print "************ REMOTE CONSOLE: CTRL-] TO QUIT ********"
+    while not stop:
+        try:
+            data = sock.recv(1)
+            os.write(1, data)
+        except socket.error, error:
+            if error[0] != errno.EINTR:
+                raise
+    print
+    print "************ REMOTE CONSOLE EXITED *****************"
+    os.wait()
+
+def __send_to_sock(sock):
+    while 1:
+        data = os.read(0,1)
+        if ord(data[0]) == ord(']')-64:
+            break
+        sock.send(data)
+    sys.exit(0)
+
+def connect(host,port):
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                    struct.pack('ii', 0, 0))
+    sock.connect((host,port))
+
+    oattrs = termios.tcgetattr(0)
+    nattrs = termios.tcgetattr(0)
+    nattrs[3] = nattrs[3] & ~(termios.ECHO | termios.ICANON)
+    nattrs[6][termios.VMIN] = 1
+    nattrs[6][termios.VTIME] = 0
+    termios.tcsetattr(0, termios.TCSAFLUSH, nattrs)
+
+    try:
+        if os.fork():
+            signal.signal(signal.SIGCHLD, __child_death)
+            __recv_from_sock(sock)
+        else:
+            __send_to_sock(sock)
+    finally:
+        termios.tcsetattr(0, termios.TCSAFLUSH, oattrs)
+
+if __name__ == '__main__':
+    main(str(sys.argv[1]),int(sys.argv[2]))
diff --git a/tools/xenctl/lib/utils.py b/tools/xenctl/lib/utils.py
new file mode 100644 (file)
index 0000000..3f0914f
--- /dev/null
@@ -0,0 +1,1014 @@
+import os, re, socket, string, sys, tempfile
+
+##### Module variables
+
+"""Location of the Virtual Disk management database.
+   defaults to /var/db/xen_vdisks.sqlite
+"""
+VD_DB_FILE = "/var/db/xen_vdisks.sqlite"
+
+"""VBD expertise level - determines the strictness of the sanity checking.
+  This mode determines the level of complaints when disk sharing occurs
+  through the current VBD mappings.
+   0 - only allow shared mappings if both domains have r/o access (always OK)
+   1 - also allow sharing with one dom r/w and the other r/o
+   2 - allow sharing with both doms r/w
+"""
+VBD_EXPERT_MODE = 0
+
+##### Module initialisation
+
+try:
+    # try to import sqlite (not everyone will have it installed)
+    import sqlite
+except ImportError:
+    # on failure, just catch the error, don't do anything
+    pass
+
+
+##### Networking-related functions
+
+def get_current_ipaddr(dev='eth0'):
+    """Return a string containing the primary IP address for the given
+    network interface (default 'eth0').
+    """
+    fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+    lines = fd.readlines()
+    for line in lines:
+        m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+                       line )
+        if m:
+            return m.group(1)
+    return None
+
+def get_current_ipmask(dev='eth0'):
+    """Return a string containing the primary IP netmask for the given
+    network interface (default 'eth0').
+    """
+    fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
+    lines = fd.readlines()
+    for line in lines:
+        m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
+                       line )
+        if m:
+            return m.group(1)
+    return None
+
+def get_current_ipgw(dev='eth0'):
+    """Return a string containing the IP gateway for the given
+    network interface (default 'eth0').
+    """
+    fd = os.popen( '/sbin/route -n' )
+    lines = fd.readlines()
+    for line in lines:
+        m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
+                       '\s+\S+\s+\S*G.*' + dev + '.*', line )
+        if m:
+            return m.group(1)
+    return None
+
+def setup_vfr_rules_for_vif(dom,vif,addr):
+    """Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
+    is expressed as a textual dotted quad, and set up appropriate routing
+    rules in Xen. No return value.
+    """
+    fd = os.open( '/proc/xen/vfr', os.O_WRONLY )
+    if ( re.search( '169\.254', addr) ):
+        os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
+                  ' srcaddrmask=255.255.255.255' +
+                  ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
+                  ' dstdom=0 dstidx=0 proto=any\n' )
+    else:
+        os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
+                  ' srcaddrmask=255.255.255.255' +
+                  ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
+                  ' dst=PHYS proto=any\n' )
+    os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
+              ' dstaddrmask=255.255.255.255' +
+              ' src=ANY' +
+              ' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
+              ' proto=any\n' )
+    os.close( fd )
+    return None
+
+def add_offset_to_ip( ip, off ):
+    l = string.split(ip,'.')
+    a = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
+         (string.atoi(l[2])<<8)  | string.atoi(l[3]) ) + off
+    
+    return '%d.%d.%d.%d' % ( ((a>>24)&0xff), ((a>>16)&0xff),
+                            ((a>>8)&0xff), (a&0xff) )
+
+def check_subnet( ip, network, netmask ):
+    l = string.split(ip,'.')
+    n_ip = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
+          (string.atoi(l[2])<<8)  | string.atoi(l[3]) ) 
+
+    l = string.split(network,'.')
+    n_net = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
+          (string.atoi(l[2])<<8)  | string.atoi(l[3]) )
+
+    l = string.split(netmask,'.')
+    n_mask = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) | 
+          (string.atoi(l[2])<<8)  | string.atoi(l[3]) )
+    
+    return (n_ip&n_mask)==(n_net&n_mask)
+
+
+##### VBD-related Functions
+
+def blkdev_name_to_number(name):
+    """Take the given textual block-device name (e.g., '/dev/sda1',
+    'hda') and return the device number used by the OS. """
+
+    if not re.match( '/dev/', name ):
+        name = '/dev/' + name
+        
+    return os.stat(name).st_rdev
+
+# lookup_blkdev_partn_info( '/dev/sda3' )
+def lookup_raw_partn(partition):
+    """Take the given block-device name (e.g., '/dev/sda1', 'hda')
+    and return a dictionary { device, start_sector,
+    nr_sectors, type }
+        device:       Device number of the given partition
+        start_sector: Index of first sector of the partition
+        nr_sectors:   Number of sectors comprising this partition
+        type:         'Disk' or identifying name for partition type
+    """
+
+    if not re.match( '/dev/', partition ):
+        partition = '/dev/' + partition
+
+    drive = re.split( '[0-9]', partition )[0]
+
+    if drive == partition:
+        fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
+        line = fd.readline()
+        if line:
+            return [ { 'device' : blkdev_name_to_number(drive),
+                       'start_sector' : long(0),
+                       'nr_sectors' : long(line) * 2,
+                       'type' : 'Disk' } ]
+        return None
+
+    # determine position on disk
+    fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
+
+    #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
+    lines = fd.readlines()
+    for line in lines:
+        m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
+                       'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
+        if m:
+            return [ { 'device' : blkdev_name_to_number(drive),
+                       'start_sector' : long(m.group(1)),
+                       'nr_sectors' : long(m.group(2)),
+                       'type' : m.group(3) } ]
+    
+    return None
+
+def lookup_disk_uname( uname ):
+    """Lookup a list of segments for either a physical or a virtual device.
+    uname [string]:  name of the device in the format \'vd:id\' for a virtual
+                     disk, or \'phy:dev\' for a physical device
+    returns [list of dicts]: list of extents that make up the named device
+    """
+    ( type, d_name ) = string.split( uname, ':' )
+
+    if type == "phy":
+        segments = lookup_raw_partn( d_name )
+    elif type == "vd":
+       segments = vd_lookup( d_name )
+
+    return segments
+
+
+##### Management of the Xen control daemon
+##### (c) Keir Fraser, University of Cambridge
+
+def xend_control_message( message ):
+    """Takes a textual control message and sends it to the 'xend' Xen
+    control daemon. Returns a dictionary containing the daemon's multi-part
+    response."""
+    tmpdir = tempfile.mkdtemp()
+    try:
+        ctl = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+        ctl.bind(tmpdir+'/sock')
+        ctl.sendto(message, '/var/run/xend/management_sock')
+        data, addr = ctl.recvfrom(2048)
+        ctl.close()
+    finally:
+        if os.path.exists(tmpdir+'/sock'):
+            os.unlink(tmpdir+'/sock')
+        if os.path.exists(tmpdir):
+            os.rmdir(tmpdir)    
+    return eval(data)
+
+
+##### VD Management-related functions
+
+##### By Mark Williamson, <mark.a.williamson@intel.com>
+##### (C) Intel Research Cambridge
+
+# TODO:
+#
+# Plenty of room for enhancement to this functionality (contributions
+# welcome - and then you get to have your name in the source ;-)...
+#
+# vd_unformat() : want facilities to unallocate virtual disk
+# partitions, possibly migrating virtual disks of them, with checks to see if
+# it's safe and options to force it anyway
+#
+# vd_create() : should have an optional argument specifying a physical
+# disk preference - useful to allocate for guest doms to do RAID
+#
+# vd_undelete() : add ability to "best effort" undelete as much of a
+# vdisk as is left in the case that some of it has already been
+# reallocated.  Some people might still be able to recover some of
+# their data this way, even if some of the disk has disappeared.
+#
+# It'd be nice if we could wipe virtual disks for security purposes -
+# should be easy to do this using dev if=/dev/{zero,random} on each
+# extent in turn.  There could be another optional flag to vd_create
+# in order to allow this.
+#
+# Error codes could be more expressive - i.e. actually tell why the
+# error occurred rather than "it broke".  Currently the code avoids
+# using exceptions to make control scripting simpler and more
+# accessible to beginners - therefore probably should just use more
+# return codes.
+#
+# Enhancements / additions to the example scripts are also welcome:
+# some people will interact with this code mostly through those
+# scripts.
+#
+# More documentation of how this stuff should be used is always nice -
+# if you have a novel configuration that you feel isn't discussed
+# enough in the HOWTO (which is currently a work in progress), feel
+# free to contribute a walkthrough, or something more substantial.
+#
+
+
+def __vd_no_database():
+    """Called when no database found - exits with an error
+    """
+    print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
+    sys.exit(1)
+
+
+def vd_format(partition, extent_size_mb):
+    """Format a partition or drive for use a virtual disk storage.
+    partition [string]: device file representing the partition
+    extent_size_mb [string]: extent size in megabytes to use on this disk
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        vd_init_db(VD_DB_FILE)
+    
+    if not re.match( '/dev/', partition ):
+        partition = '/dev/' + partition
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("select * from vdisk_part where partition = \'"
+               + partition + "\'")
+    row = cu.fetchone()
+
+    extent_size = extent_size_mb * 2048 # convert megabytes to sectors
+    
+    if not row:
+        part_info = lookup_raw_partn(partition)[0]
+        
+        cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
+                   "VALUES ( \'" + partition + "\', "
+                   + str(blkdev_name_to_number(partition))
+                   + ", " + str(extent_size) + ")")
+
+
+        cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
+                   + "WHERE vdisk_id = 0")
+        
+        max_id, = cu.fetchone()
+
+        if max_id != None:
+            new_id = max_id + 1
+        else:
+            new_id = 0
+
+        num_extents = part_info['nr_sectors'] / extent_size
+
+        for i in range(num_extents):
+            sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
+                                              part_id, part_extent_no)
+                    VALUES ("""+ str(new_id + i) + ", 0, "\
+                               + str(blkdev_name_to_number(partition))\
+                               + ", " + str(num_extents - (i + 1)) + ")"
+            cu.execute(sql)
+
+    cx.commit()
+    cx.close()
+    return 0
+
+
+def vd_create(size_mb, expiry):
+    """Create a new virtual disk.
+    size_mb [int]: size in megabytes for the new virtual disk
+    expiry [int]: expiry time in seconds from now
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    size = size_mb * 2048
+
+    cu.execute("SELECT max(vdisk_id) FROM vdisks")
+    max_id, = cu.fetchone()
+    new_id = int(max_id) + 1
+
+    # fetch a list of extents from the expired disks, along with information
+    # about their size
+    cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
+                         vdisk_extents.part_id, extent_size
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                                  NATURAL JOIN vdisk_part
+                  WHERE expires AND expiry_time <= datetime('now')
+                  ORDER BY expiry_time ASC, vdisk_extent_no DESC
+               """)  # aims to reuse the last extents
+                     # from the longest-expired disks first
+
+    allocated = 0
+
+    if expiry:
+        expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
+        expires = 1
+    else:
+        expiry_ts = "NULL"
+        expires = 0
+
+    # we'll use this to build the SQL statement we want
+    building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
+                   +" VALUES ("+str(new_id)+", "+str(size)+ ", "              \
+                   + str(expires) + ", " + expiry_ts + "); "
+
+    counter = 0
+
+    while allocated < size:
+        row = cu.fetchone()
+        if not row:
+            print "ran out of space, having allocated %d meg of %d" % (allocated, size)
+            cx.close()
+            return -1
+        
+
+        (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
+        allocated += extent_size
+        building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
+                        + ", " + "vdisk_extent_no = " + str(counter)         \
+                        + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
+                        + " AND vdisk_id = " + str(vdisk_id) + "; "
+
+        counter += 1
+        
+
+    # this will execute the SQL query we build to store details of the new
+    # virtual disk and allocate space to it print building_sql
+    cu.execute(building_sql)
+    
+    cx.commit()
+    cx.close()
+    return str(new_id)
+
+
+def vd_lookup(id):
+    """Lookup a Virtual Disk by ID.
+    id [string]: a virtual disk identifier
+    Returns [list of dicts]: a list of extents as dicts, containing fields:
+                             device : Linux device number of host disk
+                             start_sector : within the device
+                             nr_sectors : size of this extent
+                             type : set to \'VD Extent\'
+                             
+                             part_device : Linux device no of host partition
+                             part_start_sector : within the partition
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+    cu.execute("""SELECT COUNT(*)
+                  FROM vdisks
+                  WHERE (expiry_time > datetime('now') OR NOT expires)
+                              AND vdisk_id = """ + id)
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return None
+
+    cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
+    real_size, = cu.fetchone()
+  
+    # This query tells PySQLite how to convert the data returned from the
+    # following query - the use of the multiplication confuses it otherwise ;-)
+    # This row is significant to PySQLite but is syntactically an SQL comment.
+
+    cu.execute("-- types str, int, int, int")
+
+    # This SQL statement is designed so that when the results are fetched they
+    # will be in the right format to return immediately.
+    cu.execute("""SELECT partition, vdisk_part.part_id,
+                         round(part_extent_no * extent_size) as start,
+                         extent_size
+                         
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                             NATURAL JOIN vdisk_part
+                                                
+                  WHERE vdisk_extents.vdisk_id = """ + id
+               + " ORDER BY vdisk_extents.vdisk_extent_no ASC"
+               )
+
+    extent_tuples = cu.fetchall()
+
+    # use this function to map the results from the database into a dict
+    # list of extents, for consistency with the rest of the code
+    def transform ((partition, part_device, part_offset, nr_sectors)):
+        return {
+                 # the disk device this extent is on - for passing to Xen
+                 'device' : lookup_raw_partn(partition)[0]['device'],
+                 # the offset of this extent within the disk - for passing to Xen
+                 'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
+                 # extent size, in sectors
+                 'nr_sectors' : nr_sectors,
+                 # partition device this extent is on (useful to know for xenctl.utils fns)
+                 'part_device' : part_device,
+                 # start sector within this partition (useful to know for xenctl.utils fns)
+                 'part_start_sector' : part_offset,
+                 # type of this extent - handy to know
+                 'type' : 'VD Extent' }
+
+    cx.commit()
+    cx.close()
+
+    extent_dicts = map(transform, extent_tuples)
+
+    # calculate the over-allocation in sectors (happens because
+    # we allocate whole extents)
+    allocated_size = 0
+    for i in extent_dicts:
+        allocated_size += i['nr_sectors']
+
+    over_allocation = allocated_size - real_size
+
+    # trim down the last extent's length so the resulting VBD will be the
+    # size requested, rather than being rounded up to the nearest extent
+    extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
+
+    return extent_dicts
+
+
+def vd_enlarge(vdisk_id, extra_size_mb):
+    """Create a new virtual disk.
+    vdisk_id [string]   :    ID of the virtual disk to enlarge
+    extra_size_mb  [int]:    size in megabytes to increase the allocation by
+    returns  [int]      :    0 on success, otherwise non-zero
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    extra_size = extra_size_mb * 2048
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count: # no such vdisk
+        cx.close()
+        return -1
+
+    cu.execute("-- types int")
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                         NATURAL JOIN vdisk_part
+                  WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+    real_size, = cu.fetchone() # get the true allocated size
+
+    cu.execute("-- types int")
+    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+    old_size, = cu.fetchone()
+
+
+    cu.execute("--- types int")
+    cu.execute("""SELECT MAX(vdisk_extent_no)
+                  FROM vdisk_extents
+                  WHERE vdisk_id = """ + vdisk_id)
+
+    counter = cu.fetchone()[0] + 1 # this stores the extent numbers
+
+
+    # because of the extent-based allocation, the VD may already have more
+    # allocated space than they asked for.  Find out how much we really
+    # need to add.
+    add_size = extra_size + old_size - real_size
+
+    # fetch a list of extents from the expired disks, along with information
+    # about their size
+    cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
+                         vdisk_extents.part_id, extent_size
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                                  NATURAL JOIN vdisk_part
+                  WHERE expires AND expiry_time <= datetime('now')
+                  ORDER BY expiry_time ASC, vdisk_extent_no DESC
+               """)  # aims to reuse the last extents
+                     # from the longest-expired disks first
+
+    allocated = 0
+
+    building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
+                   + " WHERE vdisk_id = " + vdisk_id + "; "
+
+    while allocated < add_size:
+        row = cu.fetchone()
+        if not row:
+            cx.close()
+            return -1
+
+        (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
+        allocated += extent_size
+        building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id    \
+                        + ", " + "vdisk_extent_no = " + str(counter)         \
+                        + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
+                        + " AND vdisk_id = " + str(dead_vd_id) + "; "
+
+        counter += 1
+        
+
+    # this will execute the SQL query we build to store details of the new
+    # virtual disk and allocate space to it print building_sql
+    cu.execute(building_sql)
+    
+    cx.commit()
+    cx.close()
+    return 0
+
+
+def vd_undelete(vdisk_id, expiry_time):
+    """Create a new virtual disk.
+    vdisk_id      [int]: size in megabytes for the new virtual disk
+    expiry_time   [int]: expiry time, in seconds from now
+    returns       [int]: zero on success, non-zero on failure
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    if vdisk_id == '0': #  undeleting vdisk 0 isn't sane!
+        return -1
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
+    cu.execute("-- types int")
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                         NATURAL JOIN vdisk_part
+                  WHERE vdisks.vdisk_id = """ + vdisk_id)
+
+    real_size, = cu.fetchone() # get the true allocated size
+
+
+    cu.execute("-- types int")
+    cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
+
+    old_size, = cu.fetchone()
+
+    if real_size < old_size:
+        cx.close()
+        return -1
+
+    if expiry_time == 0:
+        expires = '0'
+    else:
+        expires = '1'
+
+    # this will execute the SQL query we build to store details of the new
+    # virtual disk and allocate space to it print building_sql
+    cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
+               + str(expiry_time) + " seconds'), expires = " + expires
+               + " WHERE vdisk_id = " + vdisk_id)
+    
+    cx.commit()
+    cx.close()
+    return 0
+
+
+
+
+def vd_list():
+    """Lists all the virtual disks registered in the system.
+    returns [list of dicts]
+    """
+    
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("""SELECT vdisk_id, size, expires, expiry_time
+                  FROM vdisks
+                  WHERE (NOT expires) OR expiry_time > datetime('now')
+               """)
+
+    ret = cu.fetchall()
+
+    cx.close()
+
+    def makedicts((vdisk_id, size, expires, expiry_time)):
+        return { 'vdisk_id' : str(vdisk_id), 'size': size,
+                 'expires' : expires, 'expiry_time' : expiry_time }
+
+    return map(makedicts, ret)
+
+
+def vd_refresh(id, expiry):
+    """Change the expiry time of a virtual disk.
+    id [string]  : a virtual disk identifier
+    expiry [int] : expiry time in seconds from now (0 = never expire)
+    returns [int]: zero on success, non-zero on failure
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+    
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
+    if expiry:
+        expires = 1
+        expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
+    else:
+        expires = 0
+        expiry_ts = "NULL"
+
+    cu.execute("UPDATE vdisks SET expires = " + str(expires)
+               + ", expiry_time = " + expiry_ts
+               + " WHERE (expiry_time > datetime('now') OR NOT expires)"
+               + " AND vdisk_id = " + id)
+
+    cx.commit()
+    cx.close()
+    
+    return 0
+
+
+def vd_delete(id):
+    """Deletes a Virtual Disk, making its extents available for future VDs.
+       id [string]   : identifier for the virtual disk to delete
+       returns [int] : 0 on success, -1 on failure (VD not found
+                       or already deleted)
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+    
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+    cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
+               + " AND (expiry_time > datetime('now') OR NOT expires)")
+    count, = cu.fetchone()
+
+    if not count:
+        cx.close()
+        return -1
+
+    cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
+               + " WHERE vdisk_id = " + id)
+
+    cx.commit()
+    cx.close()
+    
+    return 0
+
+
+def vd_freespace():
+    """Returns the amount of free space available for new virtual disks, in MB
+    returns [int] : free space for VDs in MB
+    """
+
+    if not os.path.isfile(VD_DB_FILE):
+        __vd_no_database()
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("-- types int")
+
+    cu.execute("""SELECT SUM(extent_size)
+                  FROM vdisks NATURAL JOIN vdisk_extents
+                                           NATURAL JOIN vdisk_part
+                  WHERE expiry_time <= datetime('now') AND expires""")
+
+    sum, = cu.fetchone()
+
+    cx.close()
+
+    return sum / 2048
+
+
+def vd_init_db(path):
+    """Initialise the VD SQLite database
+    path [string]: path to the SQLite database file
+    """
+
+    cx = sqlite.connect(path)
+    cu = cx.cursor()
+
+    cu.execute(
+        """CREATE TABLE vdisk_extents
+                           ( vdisk_extent_no INT,
+                             vdisk_id INT,
+                             part_id INT,
+                             part_extent_no INT )
+        """)
+
+    cu.execute(
+        """CREATE TABLE vdisk_part
+                           ( part_id INT,
+                             partition VARCHAR,
+                             extent_size INT )
+        """)
+
+    cu.execute(
+        """CREATE TABLE vdisks
+                           ( vdisk_id INT,
+                             size INT,
+                             expires BOOLEAN,
+                             expiry_time TIMESTAMP )
+        """)
+
+
+    cu.execute(
+        """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
+                       VALUES ( 0,        0,    1,       datetime('now') )
+        """)
+
+    cx.commit()
+    cx.close()
+
+    VD_DB_FILE = path
+
+
+
+def vd_cp_to_file(vdisk_id,filename):
+    """Writes the contents of a specified vdisk out into a disk file, leaving
+    the original copy in the virtual disk pool."""
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    extents = vd_lookup(vdisk_id)
+
+    if not extents:
+        return -1
+    
+    file_idx = 0 # index into source file, in sectors
+
+    for i in extents:
+        cu.execute("""SELECT partition, extent_size FROM vdisk_part
+                      WHERE part_id =  """ + str(i['part_device']))
+
+        (partition, extent_size) = cu.fetchone()
+
+        os.system("dd bs=1b if=" + partition + " of=" + filename
+                  + " skip=" + str(i['part_start_sector'])
+                  + " seek=" + str(file_idx)
+                  + " count=" + str(i['nr_sectors'])
+                  + " > /dev/null")
+
+        file_idx += i['nr_sectors']
+
+    cx.close()
+
+    return 0 # should return -1 if something breaks
+    
+
+def vd_mv_to_file(vdisk_id,filename):
+    """Writes a vdisk out into a disk file and frees the space originally
+    taken within the virtual disk pool.
+    vdisk_id [string]: ID of the vdisk to write out
+    filename [string]: file to write vdisk contents out to
+    returns [int]: zero on success, nonzero on failure
+    """
+
+    if vd_cp_to_file(vdisk_id,filename):
+        return -1
+
+    if vd_delete(vdisk_id):
+        return -1
+
+    return 0
+
+
+def vd_read_from_file(filename,expiry):
+    """Reads the contents of a file directly into a vdisk, which is
+    automatically allocated to fit.
+    filename [string]: file to read disk contents from
+    returns [string] : vdisk ID for the destination vdisk
+    """
+
+    size_bytes = os.stat(filename).st_size
+
+    (size_mb,leftover) =  divmod(size_bytes,1048580) # size in megabytes
+    if leftover > 0: size_mb += 1 # round up if not an exact number of MB
+
+    vdisk_id = vd_create(size_mb, expiry)
+
+    if vdisk_id < 0:
+        return -1
+
+    cx = sqlite.connect(VD_DB_FILE)
+    cu = cx.cursor()
+
+    cu.execute("""SELECT partition, extent_size, part_extent_no
+                  FROM vdisk_part NATURAL JOIN vdisk_extents
+                  WHERE vdisk_id =  """ + vdisk_id + """
+                  ORDER BY vdisk_extent_no ASC""")
+
+    extents = cu.fetchall()
+
+    size_sectors = size_mb * 2048 # for feeding to dd
+
+    file_idx = 0 # index into source file, in sectors
+
+    def write_extent_to_vd((partition, extent_size, part_extent_no),
+                           file_idx, filename):
+        """Write an extent out to disk and update file_idx"""
+
+        os.system("dd bs=512 if=" + filename + " of=" + partition
+                  + " skip=" + str(file_idx)
+                  + " seek=" + str(part_extent_no * extent_size)
+                  + " count=" + str(min(extent_size, size_sectors - file_idx))
+                  + " > /dev/null")
+
+        return extent_size
+
+    for i in extents:
+        file_idx += write_extent_to_vd(i, file_idx, filename)
+
+    cx.close()
+
+    return vdisk_id
+    
+
+
+
+def vd_extents_validate(new_extents,new_writeable):
+    """Validate the extents against the existing extents.
+    Complains if the list supplied clashes against the extents that
+    are already in use in the system.
+    new_extents [list of dicts]: list of new extents, as dicts
+    new_writeable [int]: 1 if they are to be writeable, 0 otherwise
+    returns [int]: either the expertise level of the mapping if it doesn't
+                   exceed VBD_EXPERT_MODE or -1 if it does (error)
+    """
+
+    import Xc # this is only needed in this function
+
+    xc = Xc.new()
+
+    ##### Probe for explicitly created virtual disks and build a list
+    ##### of extents for comparison with the ones that are being added
+
+    probe = xc.vbd_probe()
+
+    old_extents = [] # this will hold a list of all existing extents and
+                     # their writeable status, as a list of (device,
+                     # start, size, writeable?) tuples
+
+    for vbd in probe:
+        this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
+        for vbd_ext in this_vbd_extents:
+            vbd_ext['writeable'] = vbd['writeable']
+            old_extents.append(vbd_ext)
+            
+    ##### Now scan /proc/mounts for compile a list of extents corresponding to
+    ##### any devices mounted in DOM0.  This list is added on to old_extents
+
+    regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
+    fd = open('/proc/mounts', "r")
+
+    while True:
+        line = fd.readline()
+        if not line: # if we've run out of lines then stop reading
+            break
+        
+        m = regexp.match(line)
+
+        # if the regexp didn't match then it's probably a line we don't
+        # care about - skip to next line
+        if not m:
+            continue
+
+        # lookup the device
+        ext_list = lookup_raw_partn(m.group(1))
+
+        # if lookup failed, skip to next mounted device
+        if not ext_list:
+            continue
+
+        # set a writeable flag as appropriate
+        for ext in ext_list:
+            ext['writeable'] = m.group(2) == 'rw'
+
+        # now we've got here, the contents of ext_list are in a
+        # suitable format to be added onto the old_extents list, ready
+        # for checking against the new extents
+
+        old_extents.extend(ext_list)
+
+    fd.close() # close /proc/mounts
+
+    ##### By this point, old_extents contains a list of extents, in
+    ##### dictionary format corresponding to every extent of physical
+    ##### disk that's either part of an explicitly created VBD, or is
+    ##### mounted under DOM0.  We now check these extents against the
+    ##### proposed additions in new_extents, to see if a conflict will
+    ##### happen if they are added with write status new_writeable
+
+    level = 0 # this'll accumulate the max warning level
+
+    # Search for clashes between the new extents and the old ones
+    # Takes time O(len(new_extents) * len(old_extents))
+    for new_ext in new_extents:
+        for old_ext in old_extents:
+            if(new_ext['device'] == old_ext['device']):
+
+                new_ext_start = new_ext['start_sector']
+                new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
+                
+                old_ext_start = old_ext['start_sector']
+                old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
+                
+                if((old_ext_start <= new_ext_start <= old_ext_end) or
+                   (old_ext_start <= new_ext_end <= old_ext_end)):
+                    if (not old_ext['writeable']) and new_writeable:
+                        level = max(1,level)
+                    elif old_ext['writeable'] and (not new_writeable):
+                        level = max(1,level)
+                    elif old_ext['writeable'] and new_writeable:
+                        level = max(2,level)
+
+
+    ##### level now holds the warning level incurred by the current
+    ##### VBD setup and we complain appropriately to the user
+
+
+    if level == 1:
+        print >> sys.stderr, """Warning: one or more hard disk extents
+         writeable by one domain are also readable by another."""
+    elif level == 2:
+        print >> sys.stderr, """Warning: one or more hard disk extents are
+         writeable by two or more domains simultaneously."""
+
+    if level > VBD_EXPERT_MODE:
+        print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
+        at the current safety level (%d).""" % VBD_EXPERT_MODE
+        level = -1
+
+    return level
+
diff --git a/tools/xenctl/setup.py b/tools/xenctl/setup.py
new file mode 100644 (file)
index 0000000..5dce290
--- /dev/null
@@ -0,0 +1,8 @@
+
+from distutils.core import setup, Extension
+
+setup(name = "xenctl",
+      version = "1.0",
+      packages = ["xenctl"],
+      package_dir = { "xenctl" : "lib" },
+      )